using System;
using System.IO;
using System.Net;
using System.Security.Principal;
using System.Text;
using CodePlex.TfsLibrary.RepositoryWebSvc;
using Xunit;

namespace CodePlex.TfsLibrary.ObjectModel
{
    public class TfsUtilTest
    {
        public class CombineProjectPath
        {
            [Fact]
            public void AddsTrailingSlashesAutomatically()
            {
                Assert.Equal("$/Foo/Bar/", TfsUtil.CombineProjectPath("$/Foo", "Bar"));
            }

            [Fact]
            public void ExtraTrailingSlashesAreOkay()
            {
                Assert.Equal("$/Foo/Bar/", TfsUtil.CombineProjectPath("$/Foo///", "Bar//"));
            }

            [Fact]
            public void MissingDollarSlashOkay()
            {
                Assert.Equal("$/Foo/Bar/", TfsUtil.CombineProjectPath("Foo", "Bar"));
            }

            [Fact]
            public void NonRelativePath2ReturnsPath2()
            {
                Assert.Equal("$/Bar/", TfsUtil.CombineProjectPath("/Foo", "/Bar"));
            }

            [Fact]
            public void NullPath1Throws()
            {
                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         TfsUtil.CombineProjectPath(null, "foo");
                                                     });
            }

            [Fact]
            public void NullPath2Throws()
            {
                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         TfsUtil.CombineProjectPath("foo", null);
                                                     });
            }

            [Fact]
            public void OddEdgeCases()
            {
                Assert.Equal("$/", TfsUtil.CombineProjectPath("", ""));
            }

            [Fact]
            public void SimpleCombine()
            {
                Assert.Equal("$/Foo/Bar/", TfsUtil.CombineProjectPath("$/Foo/", "Bar/"));
            }
        }

        public class GetStreamCodePage
        {
            [Fact]
            public void ANSIText()
            {
                byte[] bytes = Encoding.ASCII.GetBytes("This is a simple text string");

                using (MemoryStream stream = new MemoryStream(bytes))
                    Assert.Equal(TfsUtil.CodePage_ANSI, TfsUtil.GetStreamCodePage(stream));
            }

            [Fact]
            public void BinaryContainsEmbeddedZero()
            {
                byte[] bytes = new byte[] { 0x14, 0x19, 0x10, 0xFE, 0x00, 0x73, 0xCD, 0x97 };

                using (MemoryStream stream = new MemoryStream(bytes))
                    Assert.Equal(TfsUtil.CodePage_Binary, TfsUtil.GetStreamCodePage(stream));
            }

            [Fact]
            public void NullStreamThrows()
            {
                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         TfsUtil.GetStreamCodePage(null);
                                                     });
            }

            [Fact]
            public void UTF16()
            {
                using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetPreamble()))
                    Assert.Equal(TfsUtil.CodePage_UTF16_LittleEndian, TfsUtil.GetStreamCodePage(stream));
            }

            [Fact]
            public void UTF16BigEndian()
            {
                using (MemoryStream stream = new MemoryStream(Encoding.BigEndianUnicode.GetPreamble()))
                    Assert.Equal(TfsUtil.CodePage_UTF16_BigEndian, TfsUtil.GetStreamCodePage(stream));
            }

            [Fact]
            public void UTF32()
            {
                using (MemoryStream stream = new MemoryStream(new UTF32Encoding(false, true).GetPreamble()))
                    Assert.Equal(TfsUtil.CodePage_UTF32_LittleEndian, TfsUtil.GetStreamCodePage(stream));
            }

            [Fact]
            public void UTF32BigEndian()
            {
                using (MemoryStream stream = new MemoryStream(new UTF32Encoding(true, true).GetPreamble()))
                    Assert.Equal(TfsUtil.CodePage_UTF32_BigEndian, TfsUtil.GetStreamCodePage(stream));
            }

            [Fact]
            public void UTF8()
            {
                using (MemoryStream stream = new MemoryStream(new UTF8Encoding(true).GetPreamble()))
                    Assert.Equal(TfsUtil.CodePage_UTF8, TfsUtil.GetStreamCodePage(stream));
            }
        }

        public class GetUsername
        {
            [Fact]
            public void ConcatenatesDomainAndUsernameFromCredentials()
            {
                NetworkCredential credentials = new NetworkCredential("username", "password", "domain");

                string username = TfsUtil.GetUsername(credentials, "http://foo");

                Assert.Equal(@"domain\username", username);
            }

            [Fact]
            public void MustHaveBasicCredentials()
            {
                CredentialCache cache = new CredentialCache();
                cache.Add(new Uri("http://foo"), "NTLM", new NetworkCredential("jim", "bob"));

                Assert.Throws<ArgumentException>(delegate
                                                 {
                                                     TfsUtil.GetUsername(cache, "http://foo");
                                                 });
            }

            [Fact]
            public void ReturnsBareUsernameWhenNoDomainPresent()
            {
                NetworkCredential credentials = new NetworkCredential("username", "password");

                string username = TfsUtil.GetUsername(credentials, "http://foo");

                Assert.Equal("username", username);
            }

            [Fact]
            public void ReturnsLoggedInUserForDefaultNetworkCredentials()
            {
                string loggedInUsername = WindowsIdentity.GetCurrent().Name;

                string username = TfsUtil.GetUsername(CredentialCache.DefaultNetworkCredentials, "http://foo");

                Assert.Equal(loggedInUsername, username);
            }

            [Fact]
            public void ReturnsLoggedInUserForNullICredentials()
            {
                string loggedInUsername = WindowsIdentity.GetCurrent().Name;

                string username = TfsUtil.GetUsername(null, "http://foo");

                Assert.Equal(loggedInUsername, username);
            }

            [Fact]
            public void ReturnsLoggedInUserForNullNetworkCredential()
            {
                string loggedInUsername = WindowsIdentity.GetCurrent().Name;

                string username = TfsUtil.GetUsername(null, "http://foo");

                Assert.Equal(loggedInUsername, username);
            }
        }

        public class LocalPathToServerPath
        {
            [Fact]
            public void LocalItemNotContainedInBasePathThrows()
            {
                Assert.Throws<ArgumentException>(
                    delegate
                    {
                        TfsUtil.LocalPathToServerPath("$/Foo", @"C:\Bar", @"C:\Baz\Biff", ItemType.Folder);
                    });
            }

            [Fact]
            public void ReturnsBaseServerPathIfBaseLocalPathEqualsLocalItemPath()
            {
                string result = TfsUtil.LocalPathToServerPath("$/Foo", @"C:\Bar", @"C:\Bar", ItemType.Folder);

                Assert.Equal("$/Foo", result);
            }

            [Fact]
            public void SimpleConversion_File()
            {
                string result = TfsUtil.LocalPathToServerPath("$/Foo", @"C:\Bar", @"C:\Bar\baz.txt", ItemType.File);

                Assert.Equal("$/Foo/baz.txt", result);
            }

            [Fact]
            public void SimpleConversion_Folder()
            {
                string result = TfsUtil.LocalPathToServerPath("$/Foo", @"C:\Bar", @"C:\Bar\Baz", ItemType.Folder);

                Assert.Equal("$/Foo/Baz", result);
            }
        }

        public class LocalUpdateToLocalVersionUpdate
        {
            [Fact]
            public void Conversion()
            {
                LocalUpdate update = LocalUpdate.FromLocal(1, "2", 3);

                LocalVersionUpdate versionUpdate = TfsUtil.LocalUpdateToLocalVersionUpdate(update);

                Assert.Equal(1, versionUpdate.itemid);
                Assert.Equal("2", versionUpdate.tlocal);
                Assert.Equal(3, versionUpdate.lver);
            }
        }

        public class PendRequestToChangeRequest
        {
            [Fact]
            public void AddFile()
            {
                ChangeRequest result = TfsUtil.PendRequestToChangeRequest(PendRequest.AddFile("file", 1));

                Assert.Equal("file", result.item.item);
                Assert.Equal(RequestType.Add, result.req);
                Assert.Equal(ItemType.File, result.type);
                Assert.Equal(1, result.enc);
            }

            [Fact]
            public void AddFolder()
            {
                ChangeRequest result = TfsUtil.PendRequestToChangeRequest(PendRequest.AddFolder("folder"));

                Assert.Equal("folder", result.item.item);
                Assert.Equal(RequestType.Add, result.req);
                Assert.Equal(ItemType.Folder, result.type);
                Assert.Equal(TfsUtil.CodePage_ANSI, result.enc);
            }

            [Fact]
            public void Copy()
            {
                ChangeRequest result = TfsUtil.PendRequestToChangeRequest(PendRequest.Copy("copy", "target"));

                Assert.Equal("copy", result.item.item);
                Assert.Equal("target", result.target);
                Assert.Equal(RequestType.Branch, result.req);
            }

            [Fact]
            public void Delete()
            {
                ChangeRequest result = TfsUtil.PendRequestToChangeRequest(PendRequest.Delete("delete"));

                Assert.Equal("delete", result.item.item);
                Assert.Equal(RequestType.Delete, result.req);
            }

            [Fact]
            public void Edit()
            {
                ChangeRequest result = TfsUtil.PendRequestToChangeRequest(PendRequest.Edit("edit"));

                Assert.Equal("edit", result.item.item);
                Assert.Equal(RequestType.Edit, result.req);
                Assert.Equal(LockLevel.None, result.@lock);
            }

            [Fact]
            public void NullRequestThrows()
            {
                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         TfsUtil.PendRequestToChangeRequest(null);
                                                     });
            }

            [Fact]
            public void Rename()
            {
                ChangeRequest result = TfsUtil.PendRequestToChangeRequest(PendRequest.Rename("rename", "target"));

                Assert.Equal("rename", result.item.item);
                Assert.Equal("target", result.target);
                Assert.Equal(RequestType.Rename, result.req);
            }
        }

        public class ServerPathToLocalPath
        {
            [Fact]
            public void NormalizedBaseServerPathToRemoveTrailingSlash()
            {
                string result = TfsUtil.ServerPathToLocalPath("$/Foo/", @"C:\Bar", "$/Foo");

                Assert.Equal(@"C:\Bar", result);
            }

            [Fact]
            public void ReturnsBaseLocalPathIfBaseServerPathEqualsServerItemPath()
            {
                string result = TfsUtil.ServerPathToLocalPath("$/Foo", @"C:\Bar", "$/Foo");

                Assert.Equal(@"C:\Bar", result);
            }

            [Fact]
            public void ServerItemNotContainedInBasePathThrows()
            {
                Assert.Throws<ArgumentException>(
                    delegate
                    {
                        TfsUtil.ServerPathToLocalPath("$/Foo", @"C:\Bar", "$/Other/Server/Path");
                    });
            }

            [Fact]
            public void SimpleConversion()
            {
                string result = TfsUtil.ServerPathToLocalPath("$/Foo", @"C:\Bar", "$/Foo/Baz");

                Assert.Equal(@"C:\Bar\Baz", result);
            }
        }

        public class SetupWebRequest
        {
            [Fact]
            public void DoesNotThrowForNonHttpWebRequest()
            {
                WebRequest request = WebRequest.Create("ftp://localhost");

                TfsUtil.SetupWebRequest(request);
            }

            [Fact]
            public void DoesNotUseDefaultCredentialsWhenPassedValidCredentials()
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost");

                TfsUtil.SetupWebRequest(request, new NetworkCredential());

                Assert.False(request.UseDefaultCredentials);
            }

            [Fact]
            public void SetsPropertiesForWebRequest()
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost");

                TfsUtil.SetupWebRequest(request);

                Assert.True(request.UseDefaultCredentials);
                Assert.False(request.ServicePoint.UseNagleAlgorithm);
                Assert.False(request.SendChunked);
                Assert.False(request.Pipelined);
                Assert.True(request.KeepAlive);
                Assert.False(request.PreAuthenticate);
                Assert.True(request.UnsafeAuthenticatedConnectionSharing);
                Assert.NotNull(request.ServicePoint.BindIPEndPointDelegate);
                Assert.Null(request.ConnectionGroupName);
            }
        }
    }
}